﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;

namespace DefensiveShield
{
    public static partial class OS
    {
        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- READ CONFIG INFO FROM FILE--
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Эта функция считывает служебную информацию системы из файла
             * 
             * Функция загружает из зашифрованного вида всю информацию о:
             * <ol>
             *      <li> Пользователях
             *      <li> Зарегистрированных объектах файловой системы
             *      <li> Объектах, добавленных в список проверки целостности
             *      <li> Администраторе системы, а также ключе и т.д.    
             * </ol>
             * 
             * В качестве ключа шифрования берётся глобальный ключ системы
            @return Код ошибки, при её наличии
        */
        private static ErrorCodes ReadConfigurationFromFile()
        {
            var GlobalKeyReserve = GlobalKey;
            var IVReserve = IV;
            try
            {
                var fileName = _configurationFileName;

                var xmlDoc = XDocument.Load(fileName);
                var settings = xmlDoc.Root;
                //GlobalKey = settings.Element("GlobalKey").Value;
                //IV = Decrypt(settings.Element("IV").Value, GlobalKey);
                AdministratorName = Decrypt(settings.Element("AdministratorName").Value, GlobalKey);
                DateLastIntegritySnapshot = DateTime.Parse(Decrypt(settings.Element("DateLastIntegrityCheck").Value, GlobalKey));
                var users = settings.Element("Users");
                var userPassports = users.Elements("UserPassport").Select(e1 => e1);
                foreach (var userPassport in userPassports)
                {
                    if (!userPassports.Any() || userPassports.First().IsEmpty) break;
                    var newUser = new UserPassport();
                    newUser.UserName = Decrypt(userPassport.Element("UserName").Value, GlobalKey);
                    newUser.PasswdHash = Decrypt(userPassport.Element("PasswdHash").Value, GlobalKey);
                    newUser.GroupName = Decrypt(userPassport.Element("GroupName").Value, GlobalKey);
                    Enum.TryParse(Decrypt(userPassport.Element("SecrecyNum").Value, GlobalKey), out newUser.SecrecyNum);
                    Enum.TryParse(Decrypt(userPassport.Element("GroupRightsDirectory").Value, GlobalKey), out newUser.GroupRightsDirectory);
                    Enum.TryParse(Decrypt(userPassport.Element("OthersRightsDirectory").Value, GlobalKey), out newUser.OthersRightsDirectory);
                    Enum.TryParse(Decrypt(userPassport.Element("GroupRightsFile").Value, GlobalKey), out newUser.GroupRightsFile);
                    Enum.TryParse(Decrypt(userPassport.Element("OthersRightsFile").Value, GlobalKey), out newUser.OthersRightsFile);
                    newUser.HomeDirectory = Decrypt(userPassport.Element("HomeDirectory").Value,GlobalKey);
                    Users.Add(newUser.UserName, newUser);
                }
                
                var files = settings.Element("Files");
                var filePassports = files.Elements("FilePassport").Select(e1 => e1);
                foreach (var filePassport in filePassports)
                {
                    if (!filePassports.Any() || filePassports.First().IsEmpty) break; 
                    var newFile = new FilePassport();
                    newFile.FileName = Decrypt(filePassport.Element("FileName").Value, GlobalKey);
                    Enum.TryParse(Decrypt(filePassport.Element("FileTypeNum").Value, GlobalKey), out newFile.FileTypeNum);
                    Enum.TryParse(Decrypt(filePassport.Element("SecrecyNum").Value, GlobalKey), out newFile.SecrecyNum);
                    newFile.OwnerName = Decrypt(filePassport.Element("OwnerName").Value, GlobalKey);
                    newFile.GroupName = Decrypt(filePassport.Element("GroupName").Value, GlobalKey);
                    Enum.TryParse(Decrypt(filePassport.Element("GroupRights").Value, GlobalKey), out newFile.GroupRights);
                    Enum.TryParse(Decrypt(filePassport.Element("OtherRights").Value, GlobalKey), out newFile.OtherRights);
                    newFile.Key = Decrypt(filePassport.Element("Key").Value, GlobalKey);
                    var exclusiveRights = filePassport.Elements("ExclusiveUserAccessRights").Select(e1 => e1);
                    foreach (var exclusiveRight in exclusiveRights.TakeWhile(exclusiveRight => exclusiveRights.Any() && !exclusiveRights.First().IsEmpty))
                    {
                        Rights newR;
                        Enum.TryParse(Decrypt(exclusiveRight.Element("Rights").Value, GlobalKey), out newR);
                        newFile.ExclusiveUserAccessRights.Add(Decrypt(exclusiveRight.Element("Rigths").Value, GlobalKey), newR);
                    }
                    Files.Add(newFile.FileName, newFile);
                }
                
                var filesInt = settings.Element("FilesIntegrity");
                var fileIntegrity = filesInt.Elements("FileIntegrity").Select(e1 => e1);
                foreach (var xElement in fileIntegrity.TakeWhile(xElement => fileIntegrity.Any() && !fileIntegrity.First().IsEmpty))
                {
                    Integrity tempIntegrity;
                    Enum.TryParse(Decrypt(xElement.Element("IntegrityValue").Value, GlobalKey), out tempIntegrity);
                    FileIntegrity.Add(Decrypt(xElement.Element("FileName").Value, GlobalKey), 
                        new IntegrityState {HashValue = Decrypt(xElement.Element("HashValue").Value, GlobalKey), IntegrityValue = tempIntegrity});
                }
//                 var sessions = settings.Element("Sessions");
//                 var session = sessions.Elements("Session").Select(e1 => e1);
//                 foreach (var ses in session)
//                 {
//                     if (!session.Any() || session.First().IsEmpty) break;
//                     var ss = new OsSession();
//                     ss.FailedAuthorization = Convert.ToUInt32(Decrypt(ses.Element("FailedAuthorization").Value, GlobalKey));
//                     ss.UserName = Decrypt(ses.Element("UserName").Value, GlobalKey);
//                     Enum.TryParse(Decrypt(ses.Element("SessionSecrecy").Value, GlobalKey), out ss.SessionSecrecy);
//                     Sessions.Add(ss);
//                 }  
            }
            catch (Exception exception)
            {
                GlobalKey = GlobalKeyReserve;
                IV = IVReserve;
                //LogMessage("", "Something was wrong while reading config from file. Exception: " + exception.Message);
                return ErrorCodes.UnknownError;
            }
            //LogMessage("", "Configuration was read successfully.");
            return ErrorCodes.NoError;
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- WRITE CONFIG INFO TO FILE---
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Эта функция записывает служебную информацию системы в файл
             * 
             * Функция сохраняет в зашифрованном виде всю информацию о:
             * <ol>
             *      <li> Пользователях
             *      <li> Зарегистрированных объектах файловой системы
             *      <li> Объектах, добавленных в список проверки целостности
             *      <li> Администраторе системы, а также других объектах системы      
             * </ol>
             * 
             * В качестве ключа шифрования берётся ключ администратора
            @return Код ошибки, при её наличии
        */
        private static ErrorCodes WriteConfigurationToFile()
        {
            // Users, Files & filesIntegrity
            try
            {
                var fileName = _configurationFileName;
                var xmlDoc = new XDocument();
                var settings = new XElement("Settings");
                var users = new XElement("Users");
                settings.Add(users);
                foreach (var userPassport in Users.Values)
                {
                    var userPass = new XElement("UserPassport");
                    users.Add(userPass);
                    var name = new XElement("UserName") { Value = Encrypt(userPassport.UserName, GlobalKey) };
                    userPass.Add(name);
                    var pass = new XElement("PasswdHash") { Value = Encrypt(userPassport.PasswdHash, GlobalKey) };
                    userPass.Add(pass);
                    var groupname = new XElement("GroupName") { Value = Encrypt(userPassport.GroupName, GlobalKey) };
                    userPass.Add(groupname);
                    var secrecynum = new XElement("SecrecyNum") { Value = Encrypt(userPassport.SecrecyNum.ToString(), GlobalKey) };
                    userPass.Add(secrecynum);
                    var grouprightsdir = new XElement("GroupRightsDirectory") { Value = Encrypt(userPassport.GroupRightsDirectory.ToString(), GlobalKey) };
                    userPass.Add(grouprightsdir);
                    var otherrightsdir = new XElement("OthersRightsDirectory") { Value = Encrypt(userPassport.OthersRightsDirectory.ToString(), GlobalKey) };
                    userPass.Add(otherrightsdir);
                    var grouprightsfile = new XElement("GroupRightsFile") { Value = Encrypt(userPassport.GroupRightsFile.ToString(), GlobalKey) };
                    userPass.Add(grouprightsfile);
                    var otherrightfile = new XElement("OthersRightsFile") { Value = Encrypt(userPassport.OthersRightsFile.ToString(), GlobalKey) };
                    userPass.Add(otherrightfile);
                    var homedir = new XElement("HomeDirectory") { Value = Encrypt(userPassport.HomeDirectory, GlobalKey) };
                    userPass.Add(homedir);
                }
                var files = new XElement("Files");
                settings.Add(files);
                foreach (var filePassport in Files.Values)
                {
                    var filePass = new XElement("FilePassport");
                    files.Add(filePass);
                    var name = new XElement("FileName") { Value = Encrypt(filePassport.FileName, GlobalKey) };
                    filePass.Add(name);
                    var fileTypeNum = new XElement("FileTypeNum") { Value = Encrypt(filePassport.FileTypeNum.ToString(), GlobalKey) };
                    filePass.Add(fileTypeNum);
                    var secrecyNum = new XElement("SecrecyNum") { Value = Encrypt(filePassport.SecrecyNum.ToString(), GlobalKey) };
                    filePass.Add(secrecyNum);
                    var ownerName = new XElement("OwnerName") { Value = Encrypt(filePassport.OwnerName, GlobalKey) };
                    filePass.Add(ownerName);
                    var groupName = new XElement("GroupName") { Value = Encrypt(filePassport.GroupName, GlobalKey) };
                    filePass.Add(groupName);
                    var groupRights = new XElement("GroupRights") { Value = Encrypt(filePassport.GroupRights.ToString(), GlobalKey) };
                    filePass.Add(groupRights);
                    var otherRights = new XElement("OtherRights") { Value = Encrypt(filePassport.OtherRights.ToString(), GlobalKey) };
                    filePass.Add(otherRights);
                    var key = new XElement("Key") { Value = Encrypt(filePassport.Key, GlobalKey) };
                    filePass.Add(key);
                    var exclusiveUserAccessRights = new XElement("ExclusiveUserAccessRights");
                    filePass.Add(exclusiveUserAccessRights);
                    foreach (var keys in filePassport.ExclusiveUserAccessRights.Keys)
                    {
                        var exclusiverights = new XElement("FileName") { Value = Encrypt(keys, GlobalKey) };
                        exclusiveUserAccessRights.Add(exclusiverights);
                        var rights2 = new XElement("Rigths")
                        {
                            Value = Encrypt(filePassport.ExclusiveUserAccessRights[keys].ToString(), GlobalKey)
                        };
                        exclusiveUserAccessRights.Add(rights2);
                    }
                }
                var filesInt = new XElement("FilesIntegrity");
                foreach (var keys in FileIntegrity.Keys)
                {
                    var fileIntegrity = new XElement("FileIntegrity");
                    var file = new XElement("FileName") { Value = Encrypt(keys, GlobalKey)};
                    var hashValue = new XElement("HashValue") { Value = Encrypt(FileIntegrity[keys].HashValue, GlobalKey) };
                    var integrityValue = new XElement("IntegrityValue") {Value = Encrypt(FileIntegrity[keys].IntegrityValue.ToString(), GlobalKey)};
                    fileIntegrity.Add(file);
                    fileIntegrity.Add(hashValue);
                    fileIntegrity.Add(integrityValue);
                    filesInt.Add(fileIntegrity);
                }
                settings.Add(filesInt);
//                 var sessions = new XElement("Sessions");
//                 settings.Add(sessions);
//                 foreach (var session in Sessions)
//                 {
//                     var ses = new XElement("Session");
//                     ses.Add(new XElement("FailedAuthorization") {Value = Encrypt(Convert.ToString(session.FailedAuthorization), GlobalKey)});
//                     ses.Add(new XElement("UserName") { Value = Encrypt(session.UserName, GlobalKey) });
//                     ses.Add(new XElement("SessionSecrecy") {Value = Encrypt(Convert.ToString(session.SessionSecrecy), GlobalKey)});
//                     sessions.Add(ses);
//                 }
                settings.Add(new XElement("AdministratorName") { Value = Encrypt(AdministratorName, GlobalKey) });
                //settings.Add(new XElement("GlobalKey") { Value = GlobalKey });
                //settings.Add(new XElement("IV") { Value = Encrypt(IV, GlobalKey) });
                settings.Add(new XElement("DateLastIntegrityCheck") { Value = Encrypt(DateLastIntegritySnapshot.ToString(CultureInfo.InvariantCulture), GlobalKey) });
                xmlDoc.Add(settings);
                xmlDoc.Save(fileName);
            }
            catch (Exception exception)
            {
                LogMessage("", "Something was wrong while writting config to file. Exception: " + exception.Message);
                return ErrorCodes.UnknownError;
            }
            LogMessage("", "Configuration was writen successfully. ");
            return ErrorCodes.NoError;
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CREATE ADMINISTRATOR -------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Эта функция создаёт запись администратора в системе
            @return Код ошибки, при её наличии
        */
        public static ErrorCodes CreateAdministrator(string login, string passwd)
        {
            AdministratorName = login;
            SystemPassword = passwd;
            var err = CreateUser(login, passwd, "Администраторы", Secrecy.TopSecret,
                Rights.Noright, Rights.Noright, Rights.Noright, Rights.Noright);
            if (err != ErrorCodes.NoError)
            {
                LogMessage("", "Error creation administrator.", "");
                MessageBox.Show("Ошибка создания администратора");
                return err;
            }
            LogMessage(login, "Successful administrator creation. ");
            
            var retVal = SaveSystemCryptoInfo();
            if (retVal != ErrorCodes.NoError)
            {
                LogMessage("", "Error saving cryptografic configurations after administrator creation. Скорее всего это хана.", _baseSystemConfigurationFileName);
                return retVal;
            }
            retVal = WriteConfigurationToFile();
            if (retVal != ErrorCodes.NoError)
            {
                LogMessage("", "Error saving configurations after administrator creation.", _configurationFileName);
                return retVal;
            }

            return err;
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CREATE USER ----------------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Эта функция создаёт нового пользователя
            @return Код ошибки, при её наличии
        */
        public static ErrorCodes CreateUser(string login, string passwd, string groupName, Secrecy secrecyNum, 
            Rights groupRightsDirectory, Rights otherRightsDirectory, Rights groupRightsFile, Rights otherRightsFile)
        {
            try
            {
                var passport = new UserPassport()
                {
                    UserName = login,
                    PasswdHash = Hash(passwd),
                    GroupName = groupName,
                    SecrecyNum = secrecyNum,
                    GroupRightsDirectory = groupRightsDirectory,
                    OthersRightsDirectory = otherRightsDirectory,
                    GroupRightsFile = groupRightsFile,
                    OthersRightsFile = otherRightsFile
                };

                passport.HomeDirectory = Directory.GetCurrentDirectory() + "\\Users\\" + passport.UserName;
                if (!Directory.Exists(passport.HomeDirectory))
                    Directory.CreateDirectory(passport.HomeDirectory);

                Users.Add(passport.UserName, passport);

                LogMessage(login, "Successful creation of the user.");
                return ErrorCodes.NoError;
            }
            catch (Exception except)
            {
                LogMessage(login, "Error in Creating new user. Exception: " + except.Message);
                return ErrorCodes.UnknownError;
            }
        }

        //---------------------------------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------- CREATE ADMINISTRATOR -------
        //---------------------------------------------------------------------------------------------------------------------------------
        /**
            @brief Эта функция проверяет, есть ли у нас права на чтение папки, в рамках той операционной системы, в которой мы работаем
             * 
             * Проверка происходит имперически - через попытку чтения содержимого директории, и если попытка не удалась, значит прав нету
             * 
            @return true - если есть доступ, false - иначе
        */
        private static bool ProgramHasReadAccessToDirectory(string directoryName)
        {
            try
            {
                var localDir = new DirectoryInfo(directoryName);
                localDir.GetFileSystemInfos();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
